package cn.demoncat.util.web.cloud.config;

import cn.demoncat.util.web.TokenUtil;
import cn.demoncat.util.web.config.WebMvcConfig;
import cn.demoncat.util.web.entity.Access;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import feign.Contract;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.optionals.OptionalDecoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.cloud.openfeign.support.SpringMvcContract;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

/**
 * 描述：Spring Cloud 架构的WEB项目的配置
 *
 * @author 延晓磊
 *
 * @since 2017年8月10日
 *
 * @see <a href="https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/>Feign</a>
 */
@Configuration
public class CloudWebConfig {

	/**
	 * Feign消息转换器
	 */
	private static final ObjectFactory<HttpMessageConverters> FEIGN_CONVERTERS = getConverters();


	/**
	 * 注册RestTemplate，使用Ribbon负载均衡
	 *
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2017年8月10日
	 */
	@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		// REST工具
		RestTemplate restTemplate = new RestTemplate();
		// 整合转换器
		restTemplate.setMessageConverters(FEIGN_CONVERTERS.getObject().getConverters());
		// Rest调用出错时，会抛出RestClientException；Hystrix执行出错时，会抛出HystrixRuntimeException
		return restTemplate;
	}
	
	/**
	 * Feign日志级别：FeignClientsConfiguration
	 * 
	 * @return 
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年1月30日
	 */
	@Bean
	public Logger.Level feignLoggerLevel() {
		// 日志级别，默认NONE
		return Logger.Level.BASIC;
	}

	/**
	 * Feign请求（前置）拦截器：FeignClientsConfiguration
	 *
	 * 注意：异步请求时无效，因为是通过当前线程获取Token
	 *
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2021年02月17日
	 */
	@Bean
	public RequestInterceptor requestInterceptor(){
		return template -> {
			// 传递用户信息
			Access access = TokenUtil.getAccess();
			if (access != null) {
				template.header(TokenUtil.TOKEN_KEY, access.getToken())
						.header(TokenUtil.ACCESS_KEY, access.getId());
			}
		};
	}

	/**
	 * Feign响应解析器：FeignClientsConfiguration.feignDecoder
	 *
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月26日
	 */
	@Bean
	public Decoder feignDecoder() {
		return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(FEIGN_CONVERTERS)));
	}

	/**
	 * Feign请求编码器：FeignClientsConfiguration.feignEncoder
	 *
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月26日
	 */
	@Bean
	public Encoder feignEncoder() {
		return new SpringEncoder(FEIGN_CONVERTERS);
	}

	/**
	 * Feign请求条款：FeignClientsConfiguration.feignContract
	 *
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月26日
	 */
	@Bean
	public Contract feignContract(List<AnnotatedParameterProcessor> parameterProcessors, ConversionService feignConversionService) {
		return new SpringMvcContract(parameterProcessors, feignConversionService);
	}

	/**
	 * 自定义HystrixConcurrencyStrategy
	 *
	 * @return
	 *
	 * @author 延晓磊
	 * @since 2022年08月18日
	 */
	@Bean
	@ConditionalOnProperty(name = "feign.hystrix.enabled")
	public HystrixConcurrencyStrategy tokenHystrixConcurrencyStrategy() {
		return new TokenHystrixConcurrencyStrategy();
	}

	/**
	 * 获取Feign消息转换器
	 *
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2019年12月26日
	 */
	private static ObjectFactory<HttpMessageConverters> getConverters() {
		// 获取SpringMvc默认消息转换器
		List<HttpMessageConverter<?>> converters = new WebMvcConfigurationSupport() {
			public List<HttpMessageConverter<?>> defaultMessageConverters() {
				return super.getMessageConverters();
			}
		}.defaultMessageConverters();
		// 清理Jackson，添加FastJSON
		converters = WebMvcConfig.getConverters(converters);
		// 创建消息转换器集
		HttpMessageConverters httpMessageConverters = new HttpMessageConverters(false, converters);
		// 包装
		return () -> httpMessageConverters;
	}
}
